﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace MyImaginaryFriends_Redux.Screens.GamePlayScreen_Classes
{
    enum FriendType
    {
        None = 0,
        Lamp = 1,
    }

    //enum FriendState
    //{
    //    NotSummoned,    //Not there, but can summon.
    //    Idle,           //Idle. Waiting.
    //    Follow,         //Following.
    //    AbilityActive,  //Healing, defending.
    //    Fighting,       //Defending Mallory.
    //    Ether           //Got put in penalty area. Wait to summon.
    //}

    class FriendEntity
    {
        #region Variables
        Texture2D t2d_SpritePic;

        Texture2D t2d_SpriteOverlay;

        Animation.SpriteAnimation FriendAnimation;

        FriendType SummonType;

        TileCollision TTFCollision;
        public TileCollision GetTTFCollision()
        {
            return TTFCollision;
        }

        #region Debug Variables

        SpriteFont DebugFont;
        bool isDebugOn = false;

        #endregion

        public bool b_IsOnGround
        {
            get { return b_isOnGround; }
        }
        bool b_isOnGround;

        public bool b_isHealing;

        public bool b_isAbilityOneActive;

        public bool b_isWaiting;

        public bool b_IsSummoned
        {
            get { return b_isSummoned; }
        }
        bool b_isSummoned;

        private bool b_isSpawned = false;

        public Vector2 v2_Position
        {
            get { return v2_position; }
            set { v2_position = value; }
        }
        Vector2 v2_position;

        public Vector2 v2_PrevPosition
        {
            get { return v2_prevPosition; }
            set { v2_prevPosition = value; }
        }
        Vector2 v2_prevPosition;

        public Vector2 V2_SpriteOverlayOrigin
        {
            get { return V2_SpriteOverlayOrigin; }
            set { V2_SpriteOverlayOrigin = value; }
        }
        Vector2 v2_SpriteOverlayOrigin;

        public Vector2 v2_Velocity
        {
            get { return v2_velocity; }
            set { v2_velocity = value; }
        }
        Vector2 v2_velocity;

        private float f_previousBottom;

        const float f_MaxDistanceFromPlayerX = 100.0f;
        const float f_MaxDistanceFromPlayerY = 20.0f;

        const float f_CloseDistanceArrivalX = 50.0f;
        const float f_CloseDistanceArrivalY = 50.0f;

        public bool b_isCollidingWith_RoomBDoorTrigger = false;

        public bool b_RequestActivate_RoomBDoorTrigger
        {
            get { return b_requestActivate_RoomBDoorTrigger; }
            set { b_requestActivate_RoomBDoorTrigger = value; }
        }
        bool b_requestActivate_RoomBDoorTrigger;

        public Rectangle rec_localBounds;

        public Rectangle BoundingRectangle
        {
            get
            {
                int i_left = (int)Math.Round(v2_position.X - FriendAnimation.Origin.X) + rec_localBounds.X;
                int i_top = ((int)Math.Round(v2_position.Y - FriendAnimation.Origin.Y) + rec_localBounds.Y);

                return new Rectangle(i_left, i_top, rec_localBounds.Width, rec_localBounds.Height);
            }
        }
        #endregion

        public FriendEntity()
        {

        }

        public void LoadContent(ContentManager content)
        {
            //conditional load based on which friend is summoned, or maybe load all and only draw based on summoned one.
            t2d_SpritePic = content.Load<Texture2D>("GameplayScreenContent/Sprites/Lampy");
            t2d_SpriteOverlay = content.Load<Texture2D>("GameplayScreenContent/Overlays/LightOverlay");

            FriendAnimation = new Animation.SpriteAnimation(content.Load<Texture2D>("GameplayScreenContent/Sprites/LampySheet"), 16, 1);
            Animation.AnimationClass animation = new Animation.AnimationClass(Color.White, 1.0f, SpriteEffects.None);

            FriendAnimation.AddAnimation("Idle", 1, 16, animation.Copy());
            FriendAnimation.FramesPerSecond = 15;

            DebugFont = content.Load<SpriteFont>("MenuContent/Arial");

            v2_SpriteOverlayOrigin = new Vector2((t2d_SpriteOverlay.Width / 2.0f), (t2d_SpriteOverlay.Height / 2.0f));

            // calculate the local bounds within the texture.
            int i_width = (int)(t2d_SpritePic.Width - 15);
            int i_left = 14;// (int)(t2d_SpritePic.Width - i_width);
            int i_height = (int)(t2d_SpritePic.Height - 16);
            int i_top = 0;// (int)(t2d_SpritePic.Height - i_height);

            rec_localBounds = new Rectangle(i_left, i_top, i_width, i_height);

            b_isHealing = false;
        }

        public void Spawn(PlayerEntity player, FriendType type)
        {
            v2_position.X = player.v2_Position.X - (t2d_SpritePic.Height / 2);
            v2_position.Y = player.v2_Position.Y + 5;

            v2_velocity = Vector2.Zero;

            FriendAnimation.Animation = "Idle";
            FriendAnimation.IsLooping = true;

            FriendAnimation.Position = v2_position;

            SummonType = type;
            b_isSpawned = true;
            b_isAbilityOneActive = false;
            b_isSummoned = true;
        }

        public void Update(GameTime gameTime, PlayerEntity player, TileMap_Colliding collisionMap)
        {
            #region Grab Player Commands

            //If player wants to summon: Spawn if not summoned.
            if (player.GetRequestSummon())
            {
                b_isSummoned = true;

                if (!b_isSpawned)
                    Spawn(player, FriendType.Lamp);//this'll go off a variable later.
            }

            if (player.GetRequestDeSummon())
            {
                b_isSummoned = false;
                b_isSpawned = false;
            }
            //---*

            #endregion

            if (b_IsSummoned)
            {
                //apply physics and collision. 
                ApplyPhysics(gameTime, collisionMap);

                ApplyAI(player);

                #region Execute Player Commands

                //---Follow / Stay Requests
                if (player.GetRequestFollow())
                {
                    //If friend is following, then proceed with normal logic.
                    b_isWaiting = false;
                }

                if (player.GetRequestStay())
                {
                    //If friend is asked to say, it will sit there and look pretty.
                    //Just idle.
                    b_isWaiting = true;
                }
                //---*

                //---Ability Requests
                if (player.GetRequestAbilityOneActive())
                {
                    b_isAbilityOneActive = true;
                    AbilityOne();
                }
                else
                {
                    b_isAbilityOneActive = false;
                }
                //---*

                #endregion

                FriendAnimation.Position = v2_position;
                FriendAnimation.Update(gameTime);
                v2_prevPosition = v2_position;
            }

        }

        private void ApplyPhysics(GameTime gameTime, TileMap_Colliding collisionMap)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            Vector2 v2_previousPosition = v2_Position;

            //follow if the ApplyAI commands you to,
            if (b_isWaiting)
                v2_velocity.Y = MathHelper.Clamp(v2_velocity.Y + Physics.GravityAcceleration * elapsed, -Physics.MaxFallSpeed, Physics.MaxFallSpeed);

            //do jump if AI command tells to.
            //I'll leave friend AI logic to you, sam.

            if (b_IsOnGround)
                v2_velocity.X *= Physics.GroundDragFactor;
            else
                v2_velocity.X *= Physics.AirDragFactor;

            //prevent entity from running faster than top speed.
            v2_velocity.X = MathHelper.Clamp(v2_velocity.X, -Physics.MaxMoveSpeed, Physics.MaxMoveSpeed);

            //apply velocity
            v2_Position += v2_velocity * elapsed;
            v2_Position = new Vector2((float)Math.Round(v2_Position.X), (float)Math.Round(v2_Position.Y));

            //handle collision.
            HandleCollision(collisionMap);

            //if collision has prevented us from moving, reset velocity.
            if (v2_Position.X == v2_previousPosition.X)
                v2_velocity.X = 0;
            if (v2_Position.Y == v2_previousPosition.Y)
                v2_velocity.Y = 0;
        }

        private void HandleCollision(TileMap_Colliding collisionMap)
        {

            b_isCollidingWith_RoomBDoorTrigger = false;

            //Get the player's bounding rectangle and find the neighboring tiles.
            Rectangle rec_Bounds = BoundingRectangle;

            int i_leftTile = (int)Math.Floor((float)rec_Bounds.Left / CollidingTile.i_TileWidth);
            int i_rightTile = (int)Math.Ceiling(((float)rec_Bounds.Right / CollidingTile.i_TileWidth)) - 1;
            int i_topTile = (int)Math.Floor((float)rec_Bounds.Top / CollidingTile.i_TileHeight);
            int i_bottomTile = (int)Math.Ceiling(((float)rec_Bounds.Bottom / CollidingTile.i_TileHeight)) - 1;

            //Reset Flag to search for ground collision.
            b_isOnGround = false;
            //b_isOnStairs = false;

            //For Each Potentially Colliding Tile...
            for (int y = i_topTile; y <= i_bottomTile; ++y)
            {
                for (int x = i_leftTile; x <= i_rightTile; ++x)
                {
                    TTFCollision = collisionMap.GetCollision(x, y);

                    #region If the Tile is a collision Tile...

                    //if it's collidable
                    if (TTFCollision != TileCollision.Passable && TTFCollision != TileCollision.SpawnPoint)//all collision type checks will happen here.
                    {
                        Rectangle rec_tileBounds = collisionMap.GetBounds(x, y);

                        Vector2 v2_depth = RectangleExtensions.GetIntersectionDepth(rec_Bounds, rec_tileBounds);

                        if (v2_depth != Vector2.Zero)//if actually colliding
                        {
                            //get the absolute value of the depth of intersection
                            float f_absDepthX = Math.Abs(v2_depth.X);
                            float f_absDepthY = Math.Abs(v2_depth.Y);

                            //resolve along the shallow axis.
                            if (f_absDepthY < f_absDepthX || TTFCollision == TileCollision.Platform)
                            {
                                //if we crossed the top of a platform / tile, we are on the ground.
                                if (f_previousBottom <= rec_tileBounds.Top)
                                    b_isOnGround = true;

                                //Ignore platforms, unless we're on the ground.
                                if ((TTFCollision == TileCollision.Impassable || b_IsOnGround))
                                {
                                    //resolve the collision along the Y axis.
                                    v2_Position = new Vector2(v2_Position.X, v2_Position.Y + v2_depth.Y);

                                    //perform further collisions with the new bounds.
                                    rec_Bounds = BoundingRectangle;
                                }

                            }

                            else if (TTFCollision == TileCollision.Impassable)//Ignore platforms completely
                            {
                                //resolve collision along the X axis.
                                v2_Position = new Vector2(v2_Position.X + v2_depth.X, v2_Position.Y);

                                //perform further collisions with the new bounds
                                rec_Bounds = BoundingRectangle;
                            }

                        }
                    }

                    #endregion

                    #region OTher Tile Collision Types

                    else if (TTFCollision == TileCollision.Passable)
                    {
                        b_isOnGround = false;
                    }
                    else if (TTFCollision == TileCollision.Ladder)
                    {
                        //Lamp can float for now.
                        //Is on ladder
                        b_isOnGround = false;
                    }
                    else if (TTFCollision == TileCollision.DiagonalRightStairs)
                    {
                        //Is colliding with stairs!
                        //b_isOnStairs = true;

                        //Add or sub 3.0f dep on direction.
                        if (v2_position.X >= v2_prevPosition.X)
                        {
                            //We are going right.
                            v2_position.Y -= 4.0f;

                        }
                        else if (v2_position.X < v2_prevPosition.X)
                        {
                            //Otherwise, we're going left.
                            v2_position.Y += 4.0f;
                        }

                        b_isOnGround = true;
                        v2_position = new Vector2(v2_position.X, v2_position.Y);
                        rec_Bounds = BoundingRectangle;
                    }
                    else if (TTFCollision == TileCollision.DiagonalLeftStairs)
                    {
                        //Is colliding with stairs.
                        //b_isOnStairs = true;


                        if (v2_position.X > v2_prevPosition.X)
                        {
                            //We are going right.
                            v2_position.Y += 4.0f;

                        }
                        else if (v2_position.X < v2_prevPosition.X)
                        {
                            //Otherwise, we're going left.
                            v2_position.Y -= 4.0f;
                        }

                        b_isOnGround = true;
                        v2_position = new Vector2(v2_position.X, v2_position.Y);
                        rec_Bounds = BoundingRectangle;
                    }

                    else if (TTFCollision == TileCollision.RoomB_DoorTrigger)
                    {
                        b_isCollidingWith_RoomBDoorTrigger = true;
                        b_requestActivate_RoomBDoorTrigger = true;
                    }
                    #endregion
                }
            }

            //set velocity to zero if on the ground.
            if (b_IsOnGround)
                v2_velocity.Y = 0.0f;

            //save the new bounds bottom
            f_previousBottom = rec_Bounds.Bottom;
        }

        private void ApplyAI(PlayerEntity player)
        {
            #region Follow
            //----Follow:     
            //-X
            if (!b_isWaiting)
            {
                if (v2_position.X < (player.v2_Position.X - f_MaxDistanceFromPlayerX))
                {
                    v2_position.X += 3.0f;
                }
                else if (v2_position.X > (player.v2_Position.X + f_MaxDistanceFromPlayerX))
                {
                    v2_position.X -= 3.0f;
                }

                //-Y
                if (v2_position.Y < (player.v2_Position.Y - f_MaxDistanceFromPlayerY))
                {
                    v2_position.Y += 3.0f;
                }
                else if (v2_position.Y > (player.v2_Position.Y + f_MaxDistanceFromPlayerY))
                {
                    v2_position.Y -= 2.0f;
                }
            }
            //----
            #endregion

            #region Lamp Light Heal
            if ((player.v2_Position.X > (v2_position.X - f_MaxDistanceFromPlayerX)) && (player.v2_Position.X < (v2_position.X + f_MaxDistanceFromPlayerX)))
            {
                //Good. Within x boundaries.

                if ((player.v2_Position.Y > (v2_position.Y - f_CloseDistanceArrivalY)) && (player.v2_Position.Y < (v2_position.Y + f_CloseDistanceArrivalY)))
                {
                    //Within y boundaries.

                    if (b_isAbilityOneActive)
                    {
                        //Play sound effect if healing.
                        b_isHealing = true;
                        player.f_HealthState += 0.10f;
                        if (player.f_HealthState > 100.0f)
                        {
                            player.f_HealthState = 100.0f;
                        }
                    }
                    else
                    {
                        b_isHealing = false;
                    }
                }
            }
            #endregion

            #region Stay here and wait.
            //----Stay.
            if (b_isWaiting)
            {
                //-X
                if (player.v2_StayHerePosition.X > v2_position.X)   //Player is right.
                {
                    v2_position.X += 1.0f;
                    if (v2_position.X > player.v2_StayHerePosition.X)
                    {
                        v2_position.X = player.v2_StayHerePosition.X;
                    }
                }
                else    //Player is left.
                {
                    v2_position.X -= 1.0f;
                    if (v2_position.X < player.v2_StayHerePosition.X)
                    {
                        v2_position.X = player.v2_StayHerePosition.X;
                    }
                }

                //-Y
                if (player.v2_StayHerePosition.Y > v2_position.Y)   //Player is right.
                {
                    v2_position.Y += 1.0f;
                    if (v2_position.Y > player.v2_StayHerePosition.Y)
                    {
                        v2_position.Y = player.v2_StayHerePosition.Y;
                    }
                }
                else    //Player is left.
                {
                    v2_position.Y -= 1.0f;
                    if (v2_position.Y < player.v2_StayHerePosition.Y)
                    {
                        v2_position.Y = player.v2_StayHerePosition.Y;
                    }
                }
            }
            //----
            #endregion

            #region Enemy Interaction

            #endregion

            //=================================================================================|
            //---To Do:
            //*Make friend aware of and avoid environmental hazards.
            //*Make friend despawn after certain amount of hits, or distance away from Mallory.
            //  -Timed respawn. (Only be able to summon again once friend stays away for a bit.)
            //  -
            //=================================================================================|
        }

        private void AbilityOne()
        {
            //Depending on friend, this will change.
            //There will be a switch case here with the Enemy Type enum.
            //For now, Lamp's only ability is to prevent enemies from coming
            //near Mallory. We should probably make a counter here that will
            //shut off the ability after 'x' amount of time.
        }

        public void Draw(SpriteBatch spriteBatch, PlayerEntity player)
        {
            if (b_isSummoned)
            {
                if (v2_position.X > player.v2_Position.X)
                {
                    if (b_isHealing)
                        FriendAnimation.Draw(spriteBatch, Color.Wheat, 1.0f, SpriteEffects.None);
                    else
                        FriendAnimation.Draw(spriteBatch, Color.White, 1.0f, SpriteEffects.None);

                    if (b_isAbilityOneActive)
                    {
                        spriteBatch.Draw(t2d_SpriteOverlay, v2_position, null, Color.Teal, 1.0f, v2_SpriteOverlayOrigin, 1.0f, SpriteEffects.None, 1.0f);
                    }
                }
                else
                {
                    if (b_isHealing)
                        FriendAnimation.Draw(spriteBatch, Color.Wheat, 1.0f, SpriteEffects.FlipHorizontally);
                    else
                        FriendAnimation.Draw(spriteBatch, Color.White, 1.0f, SpriteEffects.FlipHorizontally);

                    if (b_isAbilityOneActive)
                    {
                        spriteBatch.Draw(t2d_SpriteOverlay, v2_position, null, Color.White, 1.0f, v2_SpriteOverlayOrigin, 1.0f, SpriteEffects.None, 1.0f);
                    }
                }
            }
            //this wll be conditional based on whether it's summoned, and even if the specific one is summoned.

            //spriteBatch.DrawString(DebugFont, "Is On Ground: " + b_IsOnGround.ToString(), new Vector2(0, 0), Color.White);
        }

    }
}
